#!/bin/sh

e2e_exclusive_lock() {
  true
}

e2e_test_install() {
  k8s_cleanup_namespace "$OPERATOR_NAMESPACE"
  k8s_async_cleanup
  kubectl create namespace "$OPERATOR_NAMESPACE" 2>/dev/null || kubectl get namespace "$OPERATOR_NAMESPACE"
  kubectl create namespace "$CLUSTER_NAMESPACE" 2>/dev/null || kubectl get namespace "$CLUSTER_NAMESPACE"
}

e2e_test_uninstall() {
  kubectl delete namespace "$CLUSTER_NAMESPACE"
  delete_operator_only
  install_operator_only
  wait_pods_running "$OPERATOR_NAMESPACE" 2
}

e2e_test() {
  run_test "Check that operator can be installed" check_operator_install
  run_test "Check that operator can be upgrade with same values" check_operator_upgrade
  run_test "Check that operator can not be upgrade from an alpha version" check_operator_upgrade_alpha
  run_test "Check that operator can not be upgrade from a beta version" check_operator_upgrade_beta
  run_test "Check that operator can not be upgrade from version 0.9.5" check_operator_upgrade_0_9_5
  run_test "Check that operator can be upgrade from 1st older minor version" check_operator_upgrade_first_old_minor
  run_test "Check that operator can be upgrade from 2nd older minor version" check_operator_upgrade_second_old_minor
  run_test "Check that operator can not be upgrade with an SGCluster that uses version 0.9.5" check_operator_upgrade_with_cluster_using_0_9_5
  run_test "Check that operator can be upgrade with an SGCluster that uses 1st and 2nd older minot versions" check_operator_upgrade_with_cluster_using_first_and_second_old_versions
  run_test "Check that operator can be deleted" check_operator_delete
  run_test "Check that operator can be installed from outside" check_operator_install_outside
  run_test "Check that operator can be installed with load balancer" check_operator_install_load_balancer
  run_test "Check that operator can be installed with the '--wait' option" check_operator_install_with_wait
  run_test "Check that operator can be installed with toleration properties" check_operator_helm_tolerations
  run_test "Check that operator can be installed with serviceAcctount annotations properties" check_operator_helm_service_account_annotations
  run_test "Check that operator can be installed with service annotations properties" check_operator_helm_service_annotations
  run_test "Check that operator can be installed with affinity properties" check_operator_helm_affinity
  run_test "Check that operator can be installed with nodeSelector properties" check_operator_helm_node_selector
  run_test "Check that operator can be installed with annotations properties" check_operator_helm_annotations
}

check_operator_install() {
  install_operator_only

  check_operator_installed
}

check_operator_upgrade() {
  upgrade_operator

  check_operator_installed
}

check_operator_delete() {
  delete_operator_only


  for app in operator restapi; do
    if wait_until eval 'kubectl get -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}" -o name | wc -l | grep -qxF 0'
    then
      success "Deployment 'stackgres-${app}' was deleted."
    else
      echo "FAILED. Deployment 'stackgres-${app}' was not deleted."
    fi
  done
}

check_operator_install_outside() {
  delete_operator_only

  for app in operator restapi; do
    wait_until eval 'kubectl get -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}" -o name | wc -l | grep -qxF 0'
  done

  install_operator_only --set-string adminui.service.type=NodePort --set adminui.service.nodePort=31111

  check_operator_installed
}

check_operator_install_load_balancer() {
  delete_operator_only

  for app in operator restapi; do
    wait_until eval 'kubectl get -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}" -o name | wc -l | grep -qxF 0'
  done

  install_operator_only --set adminui.service.type=LoadBalancer

  check_operator_installed
}

check_operator_install_with_wait() {
  delete_operator_only

  for app in operator restapi; do
    wait_until eval 'kubectl get -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}" -o name | wc -l | grep -qxF 0'
  done

  install_operator_only  --wait

  check_operator_installed
}

check_operator_installed() {
  for app in operator restapi; do
    REPLICAS="$(kubectl get -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}" --template '{{ .spec.replicas }}')"
    if [ "$REPLICAS" = "1" ]; then
      success "Deployment 'stackgres-${app}' has 1 replica."
      continue
    fi

    fail "Deployment 'stackgres-${app}' was $REPLICAS replicas."
  done

  for app in operator restapi; do
    if kubectl rollout status -n "$OPERATOR_NAMESPACE" "deployment/stackgres-${app}"; then
      success "Deployment 'stackgres-${app}' was rolled out."
      continue
    fi

    fail "Deployment 'stackgres-${app}' was not rolled out."
  done
}

check_operator_upgrade_alpha() {
  kubectl label deployment -n "$OPERATOR_NAMESPACE" stackgres-operator \
    --overwrite "version=${STACKGRES_VERSION%%-*}-alpha100"
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    fail "Upgrade did not failed when previous version was alpha"
  else
    success "Upgrade failed when previous version was alpha"
  fi
}

check_operator_upgrade_beta() {
  kubectl label deployment -n "$OPERATOR_NAMESPACE" stackgres-operator \
    --overwrite "version=${STACKGRES_VERSION%%-*}-beta100"
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    fail "Upgrade did not failed when previous version was beta"
  else
    success "Upgrade failed when previous version was beta"
  fi
}

check_operator_upgrade_0_9_5() {
  local PREVIOUS_VERSION=0.9.5
  kubectl label deployment -n "$OPERATOR_NAMESPACE" stackgres-operator \
    --overwrite "version=$PREVIOUS_VERSION"
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    fail "Upgrade did not failed when previous version was 0.9.5"
  else
    success "Upgrade failed when previous version was 0.9.5"
  fi
}

check_operator_upgrade_first_old_minor() {
  local PREVIOUS_VERSION="$(
    FIRST_OLDER_MINOR_VERSION="${STACKGRES_VERSION%.*}"
    FIRST_OLDER_MINOR_VERSION="${FIRST_OLDER_MINOR_VERSION#*.}"
    printf '%s.%s.%s' \
      "${STACKGRES_VERSION%%.*}" \
      "$((FIRST_OLDER_MINOR_VERSION - 1))" \
      "${STACKGRES_VERSION##*.}"
    )"
  kubectl label deployment -n "$OPERATOR_NAMESPACE" stackgres-operator \
    --overwrite "version=$PREVIOUS_VERSION"
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    success "Upgrade did not failed when previous version was $PREVIOUS_VERSION"
  else
    fail "Upgrade failed when previous version was $PREVIOUS_VERSION"
  fi
}

check_operator_upgrade_second_old_minor() {
  local PREVIOUS_VERSION="$(
    FIRST_OLDER_MINOR_VERSION="${STACKGRES_VERSION%.*}"
    FIRST_OLDER_MINOR_VERSION="${FIRST_OLDER_MINOR_VERSION#*.}"
    printf '%s.%s.%s' \
      "${STACKGRES_VERSION%%.*}" \
      "$((FIRST_OLDER_MINOR_VERSION - 2))" \
      "${STACKGRES_VERSION##*.}"
    )"
  kubectl label deployment -n "$OPERATOR_NAMESPACE" stackgres-operator \
    --overwrite "version=$PREVIOUS_VERSION"
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    success "Upgrade did not failed when previous version was $PREVIOUS_VERSION"
  else
    fail "Upgrade failed when previous version was $PREVIOUS_VERSION"
  fi
}

check_operator_upgrade_with_cluster_using_0_9_5() {
  NODE_LABEL_KEY="$(random_string)"
  NODE_LABEL_VALUE="$(random_string)"
  local PREVIOUS_VERSION=0.9.5
  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set-string "cluster.pods.scheduling.nodeSelector.$NODE_LABEL_KEY=$NODE_LABEL_VALUE"
  kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator \
    --type=json -p='[{"op":"replace","path":"/webhooks/0/rules/0/operations/1","value":"DELETE"}]'
  kubectl annotate sgcluster -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME" \
    --overwrite "stackgres.io/operatorVersion=$PREVIOUS_VERSION"
  kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator \
    --type=json -p='[{"op":"replace","path":"/webhooks/0/rules/0/operations/1","value":"UPDATE"}]'
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    fail "Upgrade did not failed with an SGCluster that uses version 0.9.5"
  else
    success "Upgrade failed with an SGCluster that uses version 0.9.5"
  fi
  remove_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE"
}

check_operator_upgrade_with_cluster_using_first_and_second_old_versions() {
  CLUSTER_1_NAME="$(get_sgcluster_name "$SPEC_NAME-1")"
  CLUSTER_2_NAME="$(get_sgcluster_name "$SPEC_NAME-2")"
  NODE_LABEL_KEY="$(random_string)"
  NODE_LABEL_VALUE="$(random_string)"
  local PREVIOUS_VERSION_1="$(
    FIRST_OLDER_MINOR_VERSION="${STACKGRES_VERSION%.*}"
    FIRST_OLDER_MINOR_VERSION="${FIRST_OLDER_MINOR_VERSION#*.}"
    printf '%s.%s.%s' \
      "${STACKGRES_VERSION%%.*}" \
      "$((FIRST_OLDER_MINOR_VERSION - 1))" \
      "${STACKGRES_VERSION##*.}"
    )"
  local PREVIOUS_VERSION_2="$(
    SECOND_OLDER_MINOR_VERSION="${STACKGRES_VERSION%.*}"
    SECOND_OLDER_MINOR_VERSION="${SECOND_OLDER_MINOR_VERSION#*.}"
    printf '%s.%s.%s' \
      "${STACKGRES_VERSION%%.*}" \
      "$((SECOND_OLDER_MINOR_VERSION - 2))" \
      "${STACKGRES_VERSION##*.}"
    )"
  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set cluster.create=false \
    --set-string cluster.postgres.version=13.9
  create_or_replace_cluster "$CLUSTER_1_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set-string "cluster.pods.scheduling.nodeSelector.$NODE_LABEL_KEY=$NODE_LABEL_VALUE" \
    --set instanceProfiles=null \
    --set configurations.create=false \
    --set-string cluster.postgres.version=13.9
  create_or_replace_cluster "$CLUSTER_2_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set-string "cluster.pods.scheduling.nodeSelector.$NODE_LABEL_KEY=$NODE_LABEL_VALUE" \
    --set instanceProfiles=null \
    --set configurations.create=false \
    --set-string cluster.postgres.version=13.9
  kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator \
    --type=json -p='[{"op":"replace","path":"/webhooks/0/rules/0/operations/1","value":"DELETE"}]'
  kubectl annotate sgcluster -n "$CLUSTER_NAMESPACE" "$CLUSTER_1_NAME" \
    --overwrite "stackgres.io/operatorVersion=$PREVIOUS_VERSION_1"
  kubectl annotate sgcluster -n "$CLUSTER_NAMESPACE" "$CLUSTER_2_NAME" \
    --overwrite "stackgres.io/operatorVersion=$PREVIOUS_VERSION_2"
  kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator \
    --type=json -p='[{"op":"replace","path":"/webhooks/0/rules/0/operations/1","value":"UPDATE"}]'
  local EXIT_CODE RESULT
  try_function upgrade_operator
  if "$RESULT"
  then
    success "Upgrade did not failed with an SGCluster that uses version $PREVIOUS_VERSION_1 and $PREVIOUS_VERSION_2"
  else
    fail "Upgrade failed with an SGCluster that uses version $PREVIOUS_VERSION_1 and $PREVIOUS_VERSION_2"
  fi
  remove_cluster "$CLUSTER_1_NAME" "$CLUSTER_NAMESPACE"
  remove_cluster "$CLUSTER_2_NAME" "$CLUSTER_NAMESPACE"
  remove_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE"
}


check_operator_helm_tolerations() {
  EFFECT="$(random_string)"
  KEY="$(random_string)"
  VALUE="$(random_string)"
  check_operator_specific_property "--set operator.tolerations[0].effect=$EFFECT \
  --set operator.tolerations[0].key=$KEY \
  --set operator.tolerations[0].operator=Equal \
  --set operator.tolerations[0].value=$VALUE" "Deployment" "stackgres-operator" "spec.template.spec.tolerations" \
  '[
  {
    "effect": "'$EFFECT'",
    "key": "'$KEY'",
    "operator": "Equal",
    "value": "'$VALUE'"
  }
]'
}

check_operator_helm_affinity() {
  KEY="$(random_string)"
  VALUE_1="$(random_string)"
  VALUE_2="$(random_string)"
  
  check_operator_specific_property "--set operator.affinity.nodeSelector.key=$KEY \
  --set operator.affinity.nodeSelector.operator== \
  --set operator.affinity.nodeSelector.values[0]=$VALUE_1 \
  --set operator.affinity.nodeSelector.values[1]=$VALUE_2" "Deployment" "stackgres-operator" "spec.template.spec.affinity" \
    '{
      "nodeSelector": {
        "key": "'$KEY'",
        "operator": "=",
        "values": [
          "'$VALUE_1'",
          "'$VALUE_2'"
        ]
      }
    }'
}

check_operator_helm_node_selector() {
  KEY="$(random_string)"
  VALUE_1="$(random_string)"
  VALUE_2="$(random_string)"
  
  check_operator_specific_property "--set operator.nodeSelector.key=$KEY \
  --set operator.nodeSelector.operator== \
  --set operator.nodeSelector.values[0]=$VALUE_1 \
  --set operator.nodeSelector.values[1]=$VALUE_2" "Deployment" "stackgres-operator" "spec.template.spec.nodeSelector" \
    '{
      "key": "'$KEY'",
      "operator": "=",
      "values": [
        "'$VALUE_1'",
        "'$VALUE_2'"
      ]
    }'
}

check_operator_helm_annotations() {
  KEY="$(random_string)"
  VALUE="$(random_string)"
  
  check_operator_specific_property "--set operator.annotations.$KEY=$VALUE" "Deployment" "stackgres-operator" "metadata.annotations.$KEY" "$VALUE"
}

check_operator_helm_service_account_annotations() {
  KEY="$(random_string)"
  VALUE="$(random_string)"
  
  check_operator_specific_property "--set operator.serviceAccount.annotations.$KEY=$VALUE" "ServiceAccount" "stackgres-operator" "metadata.annotations.$KEY" "$VALUE"
}

check_operator_helm_service_annotations() {
  KEY="$(random_string)"
  VALUE="$(random_string)"
  
  check_operator_specific_property "--set operator.service.annotations.$KEY=$VALUE" "Service" "stackgres-operator" "metadata.annotations.$KEY" "$VALUE"
}

check_operator_specific_property() {
  local PARAMETERS="$1"
  local KIND="$2"
  local NAMES="$3"
  local KEY="$4"
  local EXPECTED_VALUE="$5"
  EXPECTED_VALUE="$(printf %s "$EXPECTED_VALUE" | yq -r .)"
  
  for NAME in $NAMES; do
    ACTUAL_VALUE="$(helm template -n "$OPERATOR_NAMESPACE" stackgres-operator "$OPERATOR_CHART_PATH" \
        $PARAMETERS | yq -r 'select( select(.kind == "'$KIND'").metadata.name == "'$NAME'")'.$KEY)"  

    if [ -z "$ACTUAL_VALUE" ] || [ "$ACTUAL_VALUE" != "$EXPECTED_VALUE" ] || [ "$ACTUAL_VALUE" == "null" ]
    then
      echo "FAILURE! Property $KEY has no value $EXPECTED_VALUE (was $ACTUAL_VALUE) on $NAME $KIND"
      return 1
    else
      echo "SUCCESS! Property $KEY has value $EXPECTED_VALUE on $NAME $KIND"
    fi
  done;
}

